using System;
using System.Collections.Generic;

namespace Dida.Test
{
	public class ProgressSegment
	{
		class ProgressRoot
		{
			public float pos = 0;
		};
		ProgressRoot root;

		float min;
		float max;
		int nSteps;

		ProgressSegment parentSegment;
		object userData = null;

		public ProgressSegment()
		{
			root = new ProgressRoot();
			parentSegment = null;

			min = 0;
			max = 1;
			nSteps = 100;
		}

		public ProgressSegment[] Split(float splitPos)
		{
			ProgressSegment[] ret = new ProgressSegment[2];
			
			ret[0] = new ProgressSegment();
			ret[1] = new ProgressSegment();

			ret[0].min = min;
			ret[0].max = ret[1].min = min + (max - min) * splitPos;
			ret[1].max = max;

			ret[0].root = ret[1].root = root;
			ret[0].parentSegment = ret[1].parentSegment = this;

			return ret;
		}

		public ProgressSegment[] Split(float[] subPartsCosts)
		{
			float total = 0;
			foreach (float v in subPartsCosts)
				total += v;

			ProgressSegment[] ret = new ProgressSegment[subPartsCosts.Length];
			float curMin = min;
			for (int i = 0; i < subPartsCosts.Length; i++)
			{
				float curMax = curMin + (subPartsCosts[i] / total) * (max - min);

				ret[i] = new ProgressSegment();
				ret[i].min = curMin;
				ret[i].max = curMax;

				ret[i].root = root;
				ret[i].parentSegment = this;

				curMin = curMax;
			}

			return ret;
		}

		public void Completed()
		{
			Update(max);
		}

		public int NumSteps
		{
			get { return nSteps; }
			set { nSteps = value; }
		}

		public void Step()
		{
			float newPos = root.pos + (max - min) / nSteps;
			if (newPos > max)
				newPos = max;
			Update(newPos);
		}

		public object UserData
		{
			get { return userData; }
			set { userData = value; }
		}

		private void Update(float newPos)
		{
			UpdatedEventArgs updatedArgs = new UpdatedEventArgs();
			updatedArgs.GlobalProgress = newPos;
			updatedArgs.GlobalStep = newPos - root.pos;

			ProgressSegment curSeg = this;
			while(curSeg != null)
			{
				if (curSeg.Updated != null)
				{
					float segScale = 1 / (curSeg.max - curSeg.min);
					updatedArgs.LocalProgress = (newPos - curSeg.min) * segScale;
					updatedArgs.LocalStep = updatedArgs.GlobalStep * segScale;

					curSeg.Updated(this, updatedArgs);
				}

				curSeg = curSeg.parentSegment;
			}

			root.pos = newPos;
		}

		public class UpdatedEventArgs : EventArgs
		{
			public float LocalProgress;
			public float LocalStep;

			public float GlobalProgress;
			public float GlobalStep;
		}
		public delegate void UpdatedEventHandler(object sender,UpdatedEventArgs args);
		public event UpdatedEventHandler Updated;
	}
}